index.tsx 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. "use client";
  2. import { loginApi, registerApi, userInfoApi } from "@/api/login";
  3. import Box from "@/components/Box";
  4. import ButtonOwn from "@/components/ButtonOwn";
  5. import MobileField from "@/components/Fields/MobileField";
  6. import { Link, useRouter } from "@/i18n/routing";
  7. import { useSystemStore } from "@/stores/useSystemStore";
  8. import { useUserInfoStore } from "@/stores/useUserInfoStore";
  9. import { emailReg, neReg } from "@/utils";
  10. import { setCookies } from "@/utils/Cookies";
  11. import {
  12. Checkbox,
  13. DatePicker,
  14. DatePickerRef,
  15. Form,
  16. Input,
  17. Radio,
  18. Space,
  19. TextArea,
  20. Toast,
  21. } from "antd-mobile";
  22. import { PickerDate } from "antd-mobile/es/components/date-picker/util";
  23. import clsx from "clsx";
  24. import dayjs from "dayjs";
  25. import { useTranslations } from "next-intl";
  26. import { useSearchParams } from "next/navigation";
  27. import { FC, RefObject, useRef, useState } from "react";
  28. interface MobileFieldProps {
  29. value?: string;
  30. onChange?: (value: string) => void;
  31. }
  32. // const MobileField: FC<MobileFieldProps> = (props) => {
  33. // const t = useTranslations("form");
  34. // const { value, onChange } = props;
  35. //
  36. // const changeHandler = (value: string) => {
  37. // let newAmount = value.replace(/[^0-9]/g, "");
  38. // if (onChange) {
  39. // onChange(newAmount);
  40. // }
  41. // };
  42. // return (
  43. // <>
  44. // <Space align="center" className={"text-[#ccc]"}>
  45. // <Space align="center">+55</Space>
  46. // <Input
  47. // placeholder={t("phone")}
  48. // maxLength={11}
  49. // value={value}
  50. // onChange={(e) => changeHandler(e)}
  51. // />
  52. // </Space>
  53. // </>
  54. // );
  55. // };
  56. const SexMobile: FC<MobileFieldProps> = (props) => {
  57. const { value, onChange } = props;
  58. const handler = () => {
  59. console.log(`🚀🚀🚀🚀🚀-> in index.tsx on 58`, 123);
  60. };
  61. return (
  62. <div className={"w-[100%] border-4"} onClick={handler}>
  63. <Space align="center" className={"border-4 text-[#ccc]"}>
  64. <div className={"h-full w-[100%]"}>{value}</div>
  65. </Space>
  66. </div>
  67. );
  68. };
  69. interface FormProps {
  70. /**
  71. * 通讯地址
  72. */
  73. address?: string;
  74. /**
  75. * 用户头像
  76. */
  77. avatar_url?: string;
  78. /**
  79. * 生日:yyyy-MM-dd
  80. */
  81. birthday?: string;
  82. /**
  83. * 邮箱地址
  84. */
  85. email?: string;
  86. /**
  87. * 真实姓名
  88. */
  89. nick_name?: string;
  90. /**
  91. * 外部ID
  92. */
  93. open_id?: string;
  94. /**
  95. * 身份护照
  96. */
  97. passport?: string;
  98. /**
  99. * 用户密码
  100. */
  101. pwd: string;
  102. /**
  103. * 推荐码
  104. */
  105. referrer_code?: string;
  106. /**
  107. * 用户名
  108. */
  109. user_name?: string;
  110. /**
  111. * 用户电话号码
  112. */
  113. user_phone: string;
  114. /**
  115. * 用户类型
  116. */
  117. user_type?: string;
  118. sex?: number;
  119. /**
  120. * 渠道链接
  121. */
  122. channel_code?: string;
  123. code_phone?: string;
  124. }
  125. interface FormInitStateTypes extends FormProps {
  126. mobile?: {
  127. preValue: string;
  128. realValue: string;
  129. };
  130. }
  131. type FormType = "login" | "register";
  132. interface Props {
  133. type?: FormType;
  134. }
  135. const FormComponent: FC<Props> = (props) => {
  136. const { type = "register" } = props;
  137. const isStrictMode = useSystemStore().identity_verify.register === 1;
  138. const { setUserInfo } = useUserInfoStore();
  139. const t = useTranslations();
  140. const searchParams = useSearchParams();
  141. const [statusText, setStatusText] = useState("");
  142. /// 密码可见
  143. const [visible, setVisible] = useState(false);
  144. const spanClassName = clsx("iconfont", {
  145. "icon-kejian": visible,
  146. "icon-bukejian": !visible,
  147. });
  148. // 是否同意协议
  149. const [checkBoxValue, setCheckBoxValue] = useState<boolean>(false);
  150. const router = useRouter();
  151. /// 初始值
  152. const params = useRef<FormInitStateTypes>({
  153. user_phone: "",
  154. mobile: { preValue: "55", realValue: "" },
  155. pwd: "",
  156. sex: 0,
  157. address: undefined,
  158. birthday: undefined,
  159. email: undefined,
  160. passport: undefined,
  161. referrer_code: undefined,
  162. });
  163. const onFinish = (values: FormInitStateTypes) => {
  164. const { mobile } = values;
  165. const newValue = {
  166. ...values,
  167. user_phone: `${mobile?.preValue}${mobile?.realValue}`,
  168. code_phone: mobile?.preValue,
  169. };
  170. delete newValue.mobile;
  171. if (isStrictMode) {
  172. strictHandler(newValue as FormProps);
  173. } else {
  174. looseHandler(newValue as FormProps);
  175. }
  176. };
  177. /// 严格模式
  178. const strictHandler = (values: FormProps) => {
  179. if (type !== "login") {
  180. if (!checkBoxValue) {
  181. Toast.show({
  182. content: t("form.readyAgreement"),
  183. });
  184. return;
  185. }
  186. values.birthday = dayjs(values.birthday).format("YYYY/MM/DD");
  187. }
  188. looseHandler(values);
  189. };
  190. const loginHandler = async (values: FormProps) => {
  191. return new Promise(async (resolve, reject) => {
  192. const loginResult = await loginApi(values).catch((error) => {
  193. let text = error ? t(`code.${error.data.code}`) : t(`code.${500}`);
  194. Toast.show({
  195. content: text,
  196. });
  197. });
  198. if (loginResult?.code === 200) {
  199. setCookies("Token", loginResult.data.token as string);
  200. const result = await userInfoApi();
  201. if (result.code === 200) {
  202. setUserInfo(result.data);
  203. resolve(result);
  204. return result;
  205. }
  206. } else {
  207. reject();
  208. }
  209. });
  210. };
  211. /// 宽松模式
  212. const looseHandler = async (values: FormProps) => {
  213. // 请求
  214. Toast.show({
  215. icon: "loading",
  216. duration: 0,
  217. });
  218. // 注册
  219. if (type === "register") {
  220. const newValues = {
  221. ...values,
  222. referrer_code: sessionStorage.getItem("shareId") ?? undefined,
  223. channel_code: localStorage.getItem("channel_code") ?? undefined,
  224. // 轮盘邀请
  225. turntable_id: Number(sessionStorage.getItem("turntable_id")) ?? undefined,
  226. turntable_user_id: Number(sessionStorage.getItem("turntable_user_id")) ?? undefined,
  227. turntable_time: Number(sessionStorage.getItem("turntable_time")) ?? undefined,
  228. };
  229. registerApi(newValues)
  230. .then(async (res) => {
  231. if (res.code === 200) {
  232. loginHandler({
  233. pwd: values.pwd,
  234. user_phone: values.user_phone,
  235. code_phone: values.code_phone,
  236. }).then(() => {
  237. router.replace("/recharge");
  238. Toast.clear();
  239. });
  240. }
  241. })
  242. .catch((error) => {
  243. if (error.data.code === 1017) {
  244. sessionStorage.removeItem("shareId");
  245. }
  246. Toast.show({
  247. content: t(`code.${error.data.code}`),
  248. });
  249. });
  250. } else {
  251. /// 登录
  252. console.log(`🚀🚀🚀🚀🚀-> in index.tsx on 257`, values);
  253. loginHandler(values).then(() => {
  254. Toast.clear();
  255. const redirect = searchParams.get("redirect")
  256. ? `/${searchParams.get("redirect")}`
  257. : "/";
  258. router.replace(redirect);
  259. });
  260. }
  261. };
  262. const onConfirm = (value: PickerDate) => {
  263. const isChildren = dayjs().subtract(18, "year").isBefore(value);
  264. if (isChildren) {
  265. Toast.show({
  266. icon: "fail",
  267. content: t("form.NotSuitableForChildren"),
  268. });
  269. }
  270. };
  271. const ageValidator = (rule: any, value: PickerDate) => {
  272. const isChildren = dayjs().subtract(18, "year").isBefore(value);
  273. if (isChildren) {
  274. return Promise.reject(new Error(rule.message));
  275. } else {
  276. return Promise.resolve();
  277. }
  278. };
  279. // 手机号验证规则
  280. const checkMobile = (_: any, value: any) => {
  281. if (value.realValue.length < 10) {
  282. return Promise.reject(new Error(t("form.phoneMinReg")));
  283. } else {
  284. return Promise.resolve();
  285. }
  286. };
  287. return (
  288. <Box className={"custom-form"}>
  289. <Form
  290. style={{
  291. "--border-bottom": "none",
  292. "--border-top": "none",
  293. "--border-inner": "none",
  294. }}
  295. initialValues={params.current}
  296. onFinish={onFinish}
  297. footer={
  298. <ButtonOwn active>
  299. {type === "login" ? t("form.loginText") : t("form.registerText")}
  300. </ButtonOwn>
  301. }
  302. >
  303. <Form.Item name="mobile" rules={[{ required: true, validator: checkMobile }]}>
  304. <MobileField />
  305. </Form.Item>
  306. <Form.Item
  307. name="pwd"
  308. label=""
  309. extra={
  310. <span className={spanClassName} onClick={() => setVisible(!visible)}></span>
  311. }
  312. rules={[
  313. { required: true, message: t("form.passwordReg") },
  314. { min: 6, max: 20, message: t("form.passwordMinReg") },
  315. ]}
  316. >
  317. <Input
  318. placeholder={t("form.password")}
  319. maxLength={20}
  320. type={visible ? "text" : "password"}
  321. />
  322. </Form.Item>
  323. {type !== "login" && isStrictMode ? (
  324. <>
  325. <Form.Item
  326. name="user_name"
  327. label=""
  328. rules={[{ required: true, message: t("form.usernameReg") }]}
  329. >
  330. <Input placeholder={t("form.username")} />
  331. </Form.Item>
  332. <Form.Item
  333. name="birthday"
  334. clickable={false}
  335. trigger={"onConfirm"}
  336. arrowIcon={<i className={"iconfont icon-xiangyou1"}></i>}
  337. onClick={(e, datePickerRef: RefObject<DatePickerRef>) => {
  338. datePickerRef.current?.open();
  339. }}
  340. rules={[
  341. { required: true, message: t("form.birthdayReg") },
  342. {
  343. message: t("form.NotSuitableForChildren"),
  344. validator: ageValidator,
  345. },
  346. ]}
  347. >
  348. <DatePicker
  349. getContainer={null}
  350. min={dayjs().subtract(50, "year").toDate()}
  351. max={dayjs().toDate()}
  352. style={{ background: "#fff", color: "#000" }}
  353. >
  354. {(value) =>
  355. value ? (
  356. dayjs(value).format("YYYY/MM/DD")
  357. ) : (
  358. <span className={"text-[#ccc]"}>{t("form.birthday")}</span>
  359. )
  360. }
  361. </DatePicker>
  362. </Form.Item>
  363. <Form.Item
  364. name="email"
  365. label=""
  366. rules={[
  367. { required: true, message: t("form.emailReg"), pattern: emailReg },
  368. ]}
  369. >
  370. <Input placeholder={t("form.email")} />
  371. </Form.Item>
  372. <Form.Item name="sex">
  373. <Radio.Group>
  374. <Radio
  375. value={0}
  376. className={"mr-[0.1rem]"}
  377. style={{ "--icon-size": "18px" }}
  378. >
  379. {t("form.sexMan")}
  380. </Radio>
  381. <Radio value={1} style={{ "--icon-size": "18px" }}>
  382. {t("form.sexWoman")}
  383. </Radio>
  384. </Radio.Group>
  385. </Form.Item>
  386. <Form.Item
  387. name="passport"
  388. label=""
  389. rules={[{ required: true, message: t("form.cardReg"), pattern: neReg }]}
  390. >
  391. <Input placeholder={t("form.card")} maxLength={11} type={"text"} />
  392. </Form.Item>
  393. <Form.Item
  394. name="address"
  395. label=""
  396. rules={[{ required: true, message: t("form.addressReg") }]}
  397. >
  398. <TextArea placeholder={t("form.address")} maxLength={40} />
  399. </Form.Item>
  400. <div className={"flex px-[0.1rem]"}>
  401. <Checkbox
  402. block
  403. style={{ "--icon-size": "16px" }}
  404. checked={checkBoxValue}
  405. onChange={(value) => setCheckBoxValue(value)}
  406. ></Checkbox>
  407. <div className={"ml-[10px] select-none break-all text-[12px]"}>
  408. {t("form.agreement")}
  409. <Link href={"/preventLaunderMoney"} className={"text-[#1677ff]"}>
  410. {t("form.moneyAgreement")}
  411. </Link>
  412. <Link href={"/terms"} className={"text-[#1677ff]"}>
  413. {t("form.serverAgreement")}
  414. </Link>
  415. {t("form.agreementAnd")}
  416. <Link href={"/gamingPolicy"} className={"text-[#1677ff]"}>
  417. {t("form.childrenAgreement")}
  418. </Link>
  419. </div>
  420. </div>
  421. </>
  422. ) : null}
  423. </Form>
  424. <div className="mb-[0.2rem] flex justify-between text-[0.12rem]">
  425. {type == "login" ? (
  426. <>
  427. <Link className={"text-[#fff]"} href="/resetPhone">
  428. {t("LoginPage.forgetPwd")}
  429. </Link>
  430. <Link className={"text-[#fff]"} href="/register">
  431. {t("LoginPage.registerGo")}
  432. </Link>
  433. </>
  434. ) : (
  435. <Link className={"w-[100%] text-center text-[#fff]"} href="/login" replace>
  436. {t("LoginPage.loginGo")}
  437. </Link>
  438. )}
  439. </div>
  440. </Box>
  441. );
  442. };
  443. export default FormComponent;